using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Windows.Markup;
using System.Windows.Data;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.IO;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using System.Xml;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;

namespace Validation.Presentation.Tests
{
	[TestClass]
	public class ValidationBindingFixture
	{
		// TODO
		// ShouldValidateValidNullObject
		// ShouldValidateValidNullValueType

		// ShouldValidateFrameworkElement
		// ShouldValidateFrameworkContentElement

		// ShouldThrowIfNullNotNullableValueTypeProperty
		// ShouldUseValueConverterFromBinding

		[TestMethod]
		public void ShouldValidateValidNonEmptyString()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding NotNullText}' />
		<TextBox Name='binding' Text='{Binding NotNullText}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.NotNullText = "Hello";

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				Assert.AreEqual("Hello", bindingValue.Value);
				Assert.AreEqual("Hello", validatedValue.Value);

				validated.SetFocus();
				validatedValue.SetValue("Bye");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsFalse(expr.HasError);

				Assert.AreEqual("Bye", bindingValue.Value);
				Assert.AreEqual("Bye", validatedValue.Value);
			});
		}

		[TestMethod]
		public void ShouldValidateInValidEmptyString()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding NotNullText}' />
		<TextBox Name='binding' Text='{Binding NotNullText}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.NotNullText = "Hello";

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				Assert.AreEqual("Hello", bindingValue.Value);
				Assert.AreEqual("Hello", validatedValue.Value);

				validated.SetFocus();
				validatedValue.SetValue("");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsTrue(expr.HasError);

				Assert.AreEqual("Hello", bindingValue.Value);
				Assert.AreEqual("", validatedValue.Value);
			});
		}

		[TestMethod]
		public void ShouldValidateInvalidConvertedValueUsingTypeConverter()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding OneToFiveInt}' />
		<TextBox Name='binding' Text='{Binding OneToFiveInt}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.OneToFiveInt = 2;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				Assert.AreEqual("2", bindingValue.Value);
				Assert.AreEqual("2", validatedValue.Value);

				validated.SetFocus();
				validatedValue.SetValue("10");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsTrue(expr.HasError);

				Assert.AreEqual("2", bindingValue.Value);
				Assert.AreEqual("10", validatedValue.Value);
			});
		}

		[TestMethod]
		public void ShouldValidateInvalidConvertedValueUsingPropertyTypeConverter()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding ConvertedValue}' />
		<TextBox Name='binding' Text='{Binding ConvertedValue}' />
	</Grid>
</Window>";

			Data.MyConverter.Called = false;
			Data context = new Data();
			context.ConvertedValue = 1;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				Assert.AreEqual("1", bindingValue.Value);
				Assert.AreEqual("1", validatedValue.Value);

				validated.SetFocus();
				validatedValue.SetValue("10");
				binding.SetFocus();

				Assert.IsTrue(Data.MyConverter.Called);

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsTrue(expr.HasError);

				Assert.AreEqual("1", bindingValue.Value);
				Assert.AreEqual("10", validatedValue.Value);
			});
		}

		[TestMethod]
		public void ShouldLogWarningIfCantLoadPropertyTypeConverter()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding InvalidConverterValue}' />
		<TextBox Name='binding' Text='{Binding InvalidConverterValue}' />
	</Grid>
</Window>";

			Data.MyConverter.Called = false;
			Data context = new Data();
			context.ConvertedValue = 1;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				LastMessageListener listener = new LastMessageListener();
				ValidationBinding.TraceSource.Listeners.Add(listener);
				ValidationBinding.TraceSource.Switch.Level = SourceLevels.All;

				validated.SetFocus();
				validatedValue.SetValue("5");
				binding.SetFocus();

				Assert.IsNotNull(listener.LastMessage);
			});
		}

		[TestMethod]
		public void ShouldLogWarningIfCantCreatePropertyTypeConverter()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding ConverterWithoutCtor}' />
		<TextBox Name='binding' Text='{Binding ConverterWithoutCtor}' />
	</Grid>
</Window>";

			Data.MyConverter.Called = false;
			Data context = new Data();
			context.ConvertedValue = 1;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				LastMessageListener listener = new LastMessageListener();
				ValidationBinding.TraceSource.Listeners.Add(listener);
				ValidationBinding.TraceSource.Switch.Level = SourceLevels.All;

				validated.SetFocus();
				validatedValue.SetValue("5");
				binding.SetFocus();

				Assert.IsNotNull(listener.LastMessage);
			});
		}

		[TestMethod]
		public void ShouldLogWarningIfPropertyTypeConverterIsNotConverter()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding ConverterNotConverter}' />
		<TextBox Name='binding' Text='{Binding ConverterNotConverter}' />
	</Grid>
</Window>";

			Data.MyConverter.Called = false;
			Data context = new Data();
			context.ConvertedValue = 1;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				LastMessageListener listener = new LastMessageListener();
				ValidationBinding.TraceSource.Listeners.Add(listener);
				ValidationBinding.TraceSource.Switch.Level = SourceLevels.All;

				validated.SetFocus();
				validatedValue.SetValue("5");
				binding.SetFocus();

				Assert.IsNotNull(listener.LastMessage);
			});
		}

		[TestMethod]
		public void ShouldNoOpOnNonValidationProperty()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding NotValidated}' />
		<TextBox Name='binding' Text='{Binding NotValidated}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.NotValidated = 5;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				validated.SetFocus();
				validatedValue.SetValue("10");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsFalse(expr.HasError);

				Assert.AreEqual("10", bindingValue.Value);
				Assert.AreEqual("10", validatedValue.Value);
			});
		}

		[TestMethod]
		public void ShouldDefaultToPropertyTypeConverterIfCustomConverterAttributeFailsToLoad()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding ConvertedByType}' />
		<TextBox Name='binding' />
	</Grid>
</Window>";

			Converted.ConvertedConverter.Called = false;
			Data context = new Data();

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);

				LastMessageListener listener = new LastMessageListener();
				ValidationBinding.TraceSource.Listeners.Add(listener);
				ValidationBinding.TraceSource.Switch.Level = SourceLevels.All;

				validated.SetFocus();
				validatedValue.SetValue("5");
				binding.SetFocus();

				Assert.IsNotNull(listener.LastMessage);
				Assert.IsTrue(Converted.ConvertedConverter.Called);
				Assert.AreEqual("5", context.ConvertedByType.Value);
			});
		}

		[TestMethod]
		public void ShouldValidateWithPropertyPath()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding Child.OneToFiveInt}' />
		<TextBox Name='binding' Text='{Binding Child.OneToFiveInt}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.Child.OneToFiveInt = 2;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				validated.SetFocus();
				validatedValue.SetValue("5");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsFalse(expr.HasError);

				Assert.AreEqual("5", bindingValue.Value);
				Assert.AreEqual("5", validatedValue.Value);

				validated.SetFocus();
				validatedValue.SetValue("10");
				binding.SetFocus();

				Assert.IsTrue(expr.HasError);

				Assert.AreEqual("5", bindingValue.Value);
				Assert.AreEqual("10", validatedValue.Value);
			});
		}

		[TestMethod]
		public void ShouldValidateNoOpWithNullDataContext()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding Child.OneToFiveInt}' />
		<TextBox Name='binding' Text='{Binding Child.OneToFiveInt}' />
	</Grid>
</Window>";

			Run(xaml, null, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				validated.SetFocus();
				validatedValue.SetValue("20");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsFalse(expr.HasError);
			});
		}

		[TestMethod]
		public void ShouldValidateNoOpWithNonExistingProperty()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding Child.FooBar}' />
		<TextBox Name='binding' Text='{Binding Child.FooBar}' />
	</Grid>
</Window>";

			Run(xaml, null, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				validated.SetFocus();
				validatedValue.SetValue("20");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsFalse(expr.HasError);
			});
		}

		[TestMethod]
		public void ShouldValidateNoOpWithNonExistingRootProperty()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding Foo.Bar}' />
		<TextBox Name='binding' Text='{Binding Foo.Bar}' />
	</Grid>
</Window>";

			Run(xaml, null, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				validated.SetFocus();
				validatedValue.SetValue("20");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsFalse(expr.HasError);
			});
		}

		[TestMethod]
		public void ShouldValidateAfterCallingValueConverter()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
		xmlns:local='clr-namespace:Validation.Presentation.Tests;assembly=Validation.Presentation.Tests'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<Grid.Resources>
			<local:HalfConverter x:Key='half'/>
		</Grid.Resources>
		<TextBox Name='validated' Text='{v:ValidationBinding OneToFiveInt, Converter={StaticResource half}}' />
		<TextBox Name='binding' Text='{Binding OneToFiveInt}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.OneToFiveInt = 2;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				validated.SetFocus();
				validatedValue.SetValue("10");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.AreEqual("5", bindingValue.Value);
				// The value in the UI component remains the double, which is 
				// constantly converted back and forth to its half.
				Assert.AreEqual("10", validatedValue.Value);
				// The underlying property value, however, is 5
				Assert.AreEqual(5, context.OneToFiveInt);
				Assert.IsFalse(expr.HasError);

				validated.SetFocus();
				validatedValue.SetValue("12");
				binding.SetFocus();

				Assert.IsTrue(expr.HasError);

				Assert.AreEqual("5", bindingValue.Value);
				Assert.AreEqual("12", validatedValue.Value);
			});
		}

		[TestMethod]
		public void ShouldShowConversionErrorIfTypeConverterThrows()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<TextBox Name='validated' Text='{v:ValidationBinding OneToFiveInt}' />
		<TextBox Name='binding' Text='{Binding OneToFiveInt}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.OneToFiveInt = 2;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				Assert.AreEqual("2", bindingValue.Value);
				Assert.AreEqual("2", validatedValue.Value);

				validated.SetFocus();
				validatedValue.SetValue("foo");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsTrue(expr.HasError);

				Assert.AreEqual("2", bindingValue.Value);
				Assert.AreEqual("foo", validatedValue.Value);
			});
		}

		[TestMethod]
		public void ShouldShowConversionErrorIfValueConverterThrows()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
		xmlns:local='clr-namespace:Validation.Presentation.Tests;assembly=Validation.Presentation.Tests'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<Grid.Resources>
			<local:ThrowingValueConverter x:Key='converter'/>
		</Grid.Resources>
		<TextBox Name='validated' Text='{v:ValidationBinding OneToFiveInt, Converter={StaticResource converter}}' />
		<TextBox Name='binding' Text='{Binding OneToFiveInt}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.OneToFiveInt = 2;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				validated.SetFocus();
				validatedValue.SetValue("10");
				binding.SetFocus();

				FrameworkElement el = (FrameworkElement)window.FindName("validated");
				BindingExpression expr = el.GetBindingExpression(TextBox.TextProperty);

				Assert.IsTrue(expr.HasError);
				Assert.AreEqual("Foo", expr.ValidationError.ErrorContent);
			});
		}

		[TestMethod]
		public void ShouldPassValueConverterParameter()
		{
			string xaml = @"
<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
		xmlns:v='clr-namespace:Microsoft.Practices.EnterpriseLibrary.Validation.Presentation;assembly=Validation.Presentation'
		xmlns:sys='clr-namespace:System;assembly=mscorlib'
		xmlns:local='clr-namespace:Validation.Presentation.Tests;assembly=Validation.Presentation.Tests'
    Title='Test' Height='300' Width='300'
    >
	<Grid>
		<Grid.Resources>
			<local:ParameterValueConverter x:Key='converter'/>
			<sys:String x:Key='param'>Foo</sys:String>
		</Grid.Resources>
		<TextBox Name='validated' Text='{v:ValidationBinding OneToFiveInt, Converter={StaticResource converter}, ConverterParameter={StaticResource param}}' />
		<TextBox Name='binding' Text='{Binding OneToFiveInt}' />
	</Grid>
</Window>";

			Data context = new Data();
			context.OneToFiveInt = 2;

			Run(xaml, context, delegate(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding)
			{
				IValueProvider validatedValue = (IValueProvider)validated.GetPattern(PatternInterface.Value);
				IValueProvider bindingValue = (IValueProvider)binding.GetPattern(PatternInterface.Value);

				validated.SetFocus();
				validatedValue.SetValue("10");
				binding.SetFocus();

				Assert.AreEqual("Foo", ParameterValueConverter.Parameter);
			});
		}


		class LastMessageListener : TraceListener
		{
			public string LastMessage;

			public override void Write(string message)
			{
				LastMessage = message;
			}

			public override void WriteLine(string message)
			{
				LastMessage = message;
			}

			public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
			{
				LastMessage = message;
			}

			public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
			{
				LastMessage = String.Format(format, args);
			}
		}

		delegate void RunHandler(Window window, TextBoxAutomationPeer validated, TextBoxAutomationPeer binding);

		private void Run(string xaml, object data, RunHandler runner)
		{
			Window window = (Window)XamlReader.Load(XmlReader.Create(new StringReader(xaml)));
			window.DataContext = data;
			window.Show();

			try
			{
				WindowAutomationPeer windowPeer = new WindowAutomationPeer(window);
				List<AutomationPeer> children = windowPeer.GetChildren();

				TextBoxAutomationPeer validated = (TextBoxAutomationPeer)children[0];
				TextBoxAutomationPeer binding = (TextBoxAutomationPeer)children[1];

				runner(window, validated, binding);
			}
			finally
			{
				window.Close();
			}
		}

		class Data : INotifyPropertyChanged
		{
			public event PropertyChangedEventHandler PropertyChanged;

			private int notValidated;

			public int NotValidated
			{
				get { return notValidated; }
				set { notValidated = value; RaiseNotify("NotValidated"); }
			}

			private int value;

			[RangeValidator(1, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive)]
			public int OneToFiveInt
			{
				get { return value; }
				set { this.value = value; RaiseNotify("OneToFiveInt"); }
			}

			private string text;

			[NotNullValidator]
			[StringLengthValidator(1, RangeBoundaryType.Inclusive, 0, RangeBoundaryType.Ignore)]
			public string NotNullText
			{
				get { return text; }
				set { text = value; RaiseNotify("NotNullText"); }
			}

			private int convertedValue;

			[RangeValidator(1, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive)]
			[TypeConverter(typeof(Data.MyConverter))]
			public int ConvertedValue
			{
				get { return convertedValue; }
				set { convertedValue = value; }
			}

			[RangeValidator(1, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive)]
			[TypeConverter("FooBar")]
			public int InvalidConverterValue
			{
				get { return convertedValue; }
				set { convertedValue = value; }
			}

			[RangeValidator(1, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive)]
			[TypeConverter(typeof(Data.ConverterNoCtor))]
			public int ConverterWithoutCtor
			{
				get { return convertedValue; }
				set { convertedValue = value; }
			}

			[RangeValidator(1, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive)]
			[TypeConverter(typeof(Data.ConverterNoConverter))]
			public int ConverterNotConverter
			{
				get { return convertedValue; }
				set { convertedValue = value; }
			}

			public class ConverterNoCtor : TypeConverter
			{
				public ConverterNoCtor(string foo)
				{
				}
			}

			public class ConverterNoConverter
			{
			}

			public class MyConverter : Int32Converter
			{
				public static bool Called;

				public override object ConvertFrom(ITypeDescriptorContext context, 
					System.Globalization.CultureInfo culture, object value)
				{
					Called = true;
					return base.ConvertFrom(context, culture, value);
				}
			}

			private Child child = new Child();

			public Child Child
			{
				get { return child; }
				set { child = value; }
			}

			private Converted c;

			[TypeConverter("FooBar")]
			public Converted ConvertedByType
			{
				get { return c; }
				set { c = value; }
			}

			void RaiseNotify(string propertyName)
			{
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		[TypeConverter(typeof(Converted.ConvertedConverter))]
		class Converted
		{
			public Converted()
			{
			}

			public Converted(string value)
			{
				this.value = value;
			}

			private string value;

			public string Value
			{
				get { return value; }
				set { this.value = value; }
			}

			public class ConvertedConverter : StringConverter
			{
				public static bool Called;

				public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
				{
					Called = true;
					return new Converted((string)value);
				}
			}
		}

		class Child : INotifyPropertyChanged
		{
			public event PropertyChangedEventHandler PropertyChanged;

			void RaiseNotify(string propertyName)
			{
				if (PropertyChanged != null)
					PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}

			int value;

			[RangeValidator(1, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive)]
			public int OneToFiveInt
			{
				get { return value; }
				set { this.value = value; RaiseNotify("OneToFiveInt"); }
			}

			private string text;

			[NotNullValidator]
			public string NotNullText
			{
				get { return text; }
				set { text = value; RaiseNotify("NotNullText"); }
			}
		}
	}

	public class ParameterValueConverter : IValueConverter
	{
		public static object Parameter;

		public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			Parameter = parameter;
			return value;
		}

		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			Parameter = parameter;
			return value;
		}
	}
	
	public class ThrowingValueConverter : IValueConverter
	{
		#region IValueConverter Members

		public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			return value.ToString();
		}

		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			throw new ArgumentException("Foo");
		}

		#endregion
	}

	public class HalfConverter : IValueConverter
	{
		public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			return (int)value * 2;
		}

		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			if (value is string)
				return int.Parse((string)value) / 2;
			else if (value is int)
				return  (int)value / 2;

			throw new InvalidOperationException();
		}
	}
}
